// Copyright 2011 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Classes used to support Message's in ProtoRpc.
 * @author joetyson@gmail.com (Joe Tyson)
 */


goog.provide('protorpc.fields');
goog.provide('protorpc.fields.BooleanField');
goog.provide('protorpc.fields.BytesField');
goog.provide('protorpc.fields.EnumField');
goog.provide('protorpc.fields.FloatField');
goog.provide('protorpc.fields.IntegerField');
goog.provide('protorpc.fields.MessageField');
goog.provide('protorpc.fields.StringField');

goog.require('protorpc.Variant');
goog.require('goog.functions');


/**
 * @param {Number} number Field number.
 * @param {Object.<string, *>} options Field options.
 * @constructor
 */
protorpc.fields.Field = function(number, options) {
  options = options || {};


  // TODO: add more validation.

  /**
   * @type {Number}
   * @private
   */
  this.number_ = number;

  if (options.repeated && options.required) {
    // TODO: throw protorpc.fields.FieldDefinitionError(
    throw Error(
        'Cannot set both repeated and required');
  }

  /**
   * @type {boolean}
   * @private
   */
  this.required_ = options['required'] || false;

  /**
   * @type {boolean}
   * @private
   */
  this.repeated_ = options['repeated'] || false;

  /**
   * @type {boolean}
   * @private
   */
  this.optional_ = !this.required_;

  /**
   * @type {*}
   * @private
   */
  this.defaultValue_ = options['defaultValue'] || null;
};


/**
 * @type {string}
 * @private
 */
protorpc.fields.Field.prototype.name_ = null;


/**
 * Possble variants for this field definition.
 * @type {Array.<protorpc.EnumValue>}
 * @private
 */
protorpc.fields.Field.prototype.variants_ = null;


/**
 * Default variant for this field definition.
 * @type {protorpc.EnumValue}
 * @private
 */
protorpc.fields.Field.prototype.defaultVariant_ = null;


/**
 * The native type for this field definition.
 * @type {Object}
 * @private
 */
protorpc.fields.Field.prototype.nativeType_ = null;


/**
 * The Message definition that contains this Field definition.
 * @type {protorpc.MessageDefinition_}
 * @private
 */
protorpc.fields.Field.prototype.messageDefinition_ = null;


/**
 * Get Message definiton that contains this field definition.
 * @return {protorpc.MessageDefinition_} the Containing message definiton
 *   for field, null if no message defines this field.
 */
protorpc.fields.Field.prototype.getMessageDefinition = function() {
  return this.messageDefiniton_;
};

protorpc.fields.Field.prototype.getNumber = function() {
  return this.number_;
};

protorpc.fields.Field.prototype.getName = function() {
  return this.name_;
};

protorpc.fields.Field.prototype.isRequired = function() {
  return this.required_;
};

protorpc.fields.Field.prototype.isRepeated = function() {
  return this.repeated_;
};

protorpc.fields.Field.prototype.getDefaultValue = function() {
  return this.defaultValue_;
};


/**
 * Validate single element of field.
 * @param {*} value a value to validate.
 */
protorpc.fields.Field.prototype.validateElement = function(value) {
  throw Error('Not implemented');
};



/**
 * Field definition for an Integer.
 *
 * @param {Number} number Field number.
 * @param {protorpc.fields.FieldOptions} options Field's options.
 * @constructor
 * @extends {protorpc.fields.Field}
 */
protorpc.fields.IntegerField = function(number, options) {
  goog.base(this, number, options);
};
goog.inherits(protorpc.fields.IntegerField, protorpc.fields.Field);


/** @inheritDoc */
protorpc.fields.IntegerField.prototype.variants_ = [
  protorpc.Variant.INT32,
  protorpc.Variant.INT64,
  protorpc.Variant.UINT32,
  protorpc.Variant.UINT64,
  protorpc.Variant.SINT32,
  protorpc.Variant.SINT64
];


/** @inheritDoc */
protorpc.fields.IntegerField.prototype.defaultVariant_ = protorpc.Variant.INT64;


/** @inheritDoc */
protorpc.fields.IntegerField.prototype.nativeType_ = Number;



/**
 * Field definition for an Float.
 *
 * @param {Number} number Field number.
 * @param {protorpc.fields.FieldOptions} options Field's options.
 * @constructor
 * @extends {protorpc.fields.Field}
 */
protorpc.fields.FloatField = function(number, options) {
  goog.base(this, number, options);
};
goog.inherits(protorpc.fields.FloatField, protorpc.fields.Field);


/** @inheritDoc */
protorpc.fields.FloatField.prototype.variants_ = [
  protorpc.Variant.FLOAT,
  protorpc.Variant.DOUBLE
];


/** @inheritDoc */
protorpc.fields.FloatField.prototype.defaultVariant_ = protorpc.Variant.DOUBLE;


/** @inheritDoc */
protorpc.fields.FloatField.prototype.nativeType_ = Number;



/**
 * Field definition for an Boolean.
 *
 * @param {Number} number Field number.
 * @param {protorpc.fields.FieldOptions} options Field's options.
 * @constructor
 * @extends {protorpc.fields.Field}
 */
protorpc.fields.BooleanField = function(number, options) {
  goog.base(this, number, options);
};
goog.inherits(protorpc.fields.BooleanField, protorpc.fields.Field);


/** @inheritDoc */
protorpc.fields.BooleanField.prototype.variants_ = [
  protorpc.Variant.BOOL
];


/** @inheritDoc */
protorpc.fields.BooleanField.prototype.defaultVariant_ = protorpc.Variant.BOOL;


/** @inheritDoc */
protorpc.fields.BooleanField.prototype.nativeType_ = Boolean;



/**
 * Field definition for byte string values.
 *
 * Note: Javascript does not have a native bytestring format, so for
 *   now this will do nothing clever.
 *
 * @param {Number} number Field number.
 * @param {protorpc.fields.FieldOptions} options Field's options.
 * @constructor
 * @extends {protorpc.fields.Field}
 */
protorpc.fields.BytesField = function(number, options) {
  goog.base(this, number, options);
};
goog.inherits(protorpc.fields.BytesField, protorpc.fields.Field);


/** @inheritDoc */
protorpc.fields.BytesField.prototype.variants_ = [
  protorpc.Variant.BYTES
];


/** @inheritDoc */
protorpc.fields.BytesField.prototype.defaultVariant_ = protorpc.Variant.BYTES;


/** @inheritDoc */
protorpc.fields.BytesField.prototype.nativeType_ = String;



/**
 * Field definition for string values.
 *
 * @param {Number} number Field number.
 * @param {protorpc.fields.FieldOptions} options Field's options.
 * @constructor
 * @extends {protorpc.fields.Field}
 */
protorpc.fields.StringField = function(number, options) {
  goog.base(this, number, options);
};
goog.inherits(protorpc.fields.StringField, protorpc.fields.Field);


/** @inheritDoc */
protorpc.fields.StringField.prototype.variants_ = [
  protorpc.Variant.STRING
];


/** @inheritDoc */
protorpc.fields.StringField.prototype.defaultVariant_ = protorpc.Variant.STRING;


/** @inheritDoc */
protorpc.fields.StringField.prototype.nativeType_ = String;



/**
 * Field definition for sub-message values.
 *
 * @param {protorpc.Message} message_type Message type for field.
 * @param {Number} number Field number.
 * @param {protorpc.fields.FieldOptions} options Field's options.
 * @constructor
 * @extends {protorpc.fields.Field}
 */
protorpc.fields.MessageField = function(message_type, number, options) {
  goog.base(this, number, options);

  this.nativeType_ = message_type;
};
goog.inherits(protorpc.fields.MessageField, protorpc.fields.Field);


/** @inheritDoc */
protorpc.fields.MessageField.prototype.variants_ = [
  protorpc.Variant.MESSAGE
];


/** @inheritDoc */
protorpc.fields.MessageField.prototype.defaultVariant_ = protorpc.Variant.MESSAGE;



/**
 * Field definition for enum values.
 *
 * @param {protorpc.Enum} enum_type Enumeration type.
 * @param {Number} number Field number.
 * @param {protorpc.fields.FieldOptions} options Field's options.
 * @constructor
 * @extends {protorpc.fields.Field}
 */
protorpc.fields.EnumField = function(enum_type, number, options) {
  goog.base(this, number, options);

  /** @inheritDoc */
  this.nativeType_ = enum_type;
};
goog.inherits(protorpc.fields.EnumField, protorpc.fields.Field);


/** @inheritDoc */
protorpc.fields.EnumField.prototype.variants_ = [
  protorpc.Variant.ENUM
];


/** @inheritDoc */
protorpc.fields.EnumField.prototype.defaultVariant_ = protorpc.Variant.ENUM;



/**
 * Helper used to lazily construct a field when the fields name is known.
 * @param {!Function} fieldCtor Field constructor.
 * @private
 * @return {!Function(...*)}
 */
protorpc.fields.defineField_ = function(fieldCtor) {
  /**
   * @param {...*} var_args Arguments to pass to field.
   * @return {!Function(...*)}
   */
  return function(var_args) {

    /**
     * Field factory called with fields name.
     * @param {string} name Filed name.
     * @return {protorpc.fields.Field}
     */
    return function(name) {
      /**
       * @type {protorpc.fields.Field}
       */
      var field = goog.functions.create(fieldCtor, var_args);
      field.name_ = name;
      return field;
    };
  };
};


goog.exportSymbol(
  'protorpc.StringField',
  protorpc.fields.defineField_(protorpc.fields.StringField));

goog.exportSymbol(
  'protorpc.IntegerField',
  protorpc.fields.defineField_(protorpc.fields.StringField));

goog.exportSymbol(
  'protorpc.EnumField',
  protorpc.fields.defineField_(protorpc.fields.EnumField));

goog.exportSymbol(
  'protorpc.BooleanField',
  protorpc.fields.defineField_(protorpc.fields.BooleanField));

goog.exportSymbol(
  'protorpc.BytesField',
  protorpc.fields.defineField_(protorpc.fields.BytesField));

goog.exportSymbol(
  'protorpc.FloatField',
  protorpc.fields.defineField_(protorpc.fields.FloatField));

goog.exportSymbol(
  'protorpc.MessageField',
  protorpc.fields.defineField_(protorpc.fields.MessageField));